package org.example.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 *
 */
//通知类

//通知类一定是作为springbean存在

@Component
@Aspect // 通知类注解
public class MyAdvice {

    /*

        关于切点表达式

        配置在接口上，所有实现类都会被切面影响
        execution(void org.example.dao.UserService.update())

        配置在实现类上，只是针对具体的这个实现类，会被切面影响
        execution(void org.example.dao.impl.UserServiceImpl.update())


        * 代表一个组件
        .. 代表N个组件

            返回值写法
                具体类型
                * 表示任意返回值类型
                不存在..

            包结构的写法
                具体包结构
                * 代表一层包
                .. 代表N层包
                    aaa.bbb.*.ccc.ddd
                        表示aaa.bbb.xxx.ccc.ddd
                    aaa.bbb..ccc.ddd
                        表示aaa.bbb.xx.xxx.x.xx.ccc.ddd

            方法名的写法
                具体的方法名
                名称前缀+*  指的是以这个指定前缀开头的任意方法名
                    get*
                        getById
                        getAll

            形参的写法
                * 代表一个参数
                .. 代表任意参数

        匹配org.example包下的任意包中的UserService类或接口中所有get开头的带有一个参数的方法
        execution（public * org.example.*.UserService.get*(*))

        匹配org包下的任意包中的UserService类或接口中所有名称为getById的方法
        execution（public User org..UserService.getById(..))


     */

    // 设置切点
    /*@Pointcut("execution(void org.example.service.UserService.update())")
    public void pt(){}*/
    //创建通知方法
    //前置通知 在业务执行前增强
    /*@Before("pt()")
    public void m1(){
        System.out.println("前置增强");
    }*/


    // 后置通知
    /*@After("pt()")
    public void m2(){
        System.out.println("后置增强");
    }*/

    // 返回后通知
    /*@AfterReturning("pt()")
    public void m3(){
        System.out.println("返回后增强");
    }*/

    // 异常后通知
    /*@AfterThrowing("pt()")
    public void m4(){
        System.out.println("异常后增强");
    }*/

    //获取切点数据 JoinPoint -------------------------------------------------
    /*@Pointcut("execution(void org.example.service.UserService.update1(..))")
    public void pt(){}*/

    //前置通知 在业务执行前增强
    /*@Before("pt()")
    public void m1(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println("前置增强,取得业务参数："+ Arrays.toString(args));
    }*/


    // 后置通知
    /*@After("pt()")
    public void m2(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println("后置增强,取得业务参数："+Arrays.toString(args));
    }*/

    /*@Pointcut("execution(* org.example.service.UserService.update2(..))")
    public void pt(){}*/
    // 返回后通知
   /*@AfterReturning(value = "pt()",returning = "s")
    public void m3(String s){
        System.out.println("返回后增强,取得返回值："+s);
    }*/

    // 异常后通知
    /*@AfterThrowing(value = "pt()",throwing = "t")
    public void m4(Throwable t){
        System.out.println("异常后增强,接收到异常信息"+t);
    }*/


    //环绕通知 -------------------------------------------------------------------------
    //以后用的多 能做的事也多
    /*
        环绕通知 与 同时添加前置与后置通知的区别 就在于
        核心业务的调用时机，由环绕通知方法来决定
     */
    /*@Pointcut("execution(* org.example.service.UserService.update2(..))")
    public void pt(){}

    @Around("pt()")
    public Object m1(ProceedingJoinPoint pjp) throws Throwable {

        System.out.println("环绕通知开始");
        // 调用核心业务(让zs送花)
        Object result = pjp.proceed();

        Signature signature = pjp.getSignature();
        System.out.println("1 --- "+ signature.getDeclaringTypeName());
        System.out.println("2 --- "+ signature.getName());
        System.out.println("环绕通知结束");

        return result;
    }*/


    //实战应用 测试接口万次执行效率---------------------------------------------------

    @Around("execution(* org.example.service.UserService.get*(..))")
    public void m1(ProceedingJoinPoint pjp) throws Throwable {

        // 取得方法名
        Signature signature = pjp.getSignature();
        String methodName = signature.getName();

        // 起始时间戳
        long start = System.currentTimeMillis();

        // 执行10万次业务
        for (int i = 0; i < 100000; i++) {
            // 执行核心业务
            pjp.proceed();
        }


        // 结束时间戳
        long end = System.currentTimeMillis();
        //消耗毫秒数
        long milliSecond = end - start;

        System.out.println(methodName+"方法经过10万次执行消耗的毫秒数为："+milliSecond);

    }


}



























